home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / edit / xvisrc.zip / UNDO.C < prev    next >
C/C++ Source or Header  |  1992-07-28  |  21KB  |  879 lines

  1. /* Copyright (c) 1990,1991,1992 Chris and John Downey */
  2. #ifndef lint
  3. static char *sccsid = "@(#)undo.c    2.2 (Chris & John Downey) 8/28/92";
  4. #endif
  5.  
  6. /***
  7.  
  8. * program name:
  9.     xvi
  10. * function:
  11.     PD version of UNIX "vi" editor, with extensions.
  12. * module name:
  13.     undo.c
  14. * module function:
  15.     Code to implement "undo" command.
  16.  
  17. * usage:
  18.     We provide several primitive functions for "do"ing things,
  19.     and an "undo" function to restore the previous state.
  20.  
  21.     Normally, a primitive function is simply called, and will
  22.     automatically throw away the old previous state before
  23.     saving the current one and then making the change.
  24.  
  25.     Alternatively, it is possible to bracket lots of changes
  26.     between calls to the start_command() and end_command()
  27.     functions; more global changes can then be effected,
  28.     and undo still works as it should.  This is used for the
  29.     "global" command, for multi-line substitutes, and for
  30.     insert mode.
  31.  
  32. * history:
  33.     STEVIE - ST Editor for VI Enthusiasts, Version 3.10
  34.     Originally by Tim Thompson (twitch!tjt)
  35.     Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
  36.     Heavily modified by Chris & John Downey
  37.  
  38. ***/
  39.  
  40. #include "xvi.h"
  41.  
  42. static    void    save_position P((Xviwin *));
  43. static    void    free_changes P((Change *));
  44. static    void    report P((Xviwin *));
  45.  
  46. /*
  47.  * This variable holds the total number of added/deleted lines
  48.  * for a change; it is used for reporting (the "report" parameter).
  49.  */
  50. static    long    total_lines;
  51.  
  52. void
  53. init_undo(buffer)
  54. Buffer    *buffer;
  55. {
  56.     /*
  57.      * Initialise the undo-related variables in the Buffer.
  58.      */
  59.     buffer->b_nlevels = 0;
  60.     buffer->b_change = NULL;
  61.  
  62.     /*
  63.      * Set line numbers.
  64.      */
  65.     buffer->b_line0->l_number = 0;
  66.     buffer->b_file->l_number = 1;
  67.     buffer->b_lastline->l_number = MAX_LINENO;
  68. }
  69.  
  70. /*
  71.  * Start a command. This routine may be called many times;
  72.  * what it does is increase the variable which shows how
  73.  * many times it has been called.  The end_command() then
  74.  * decrements the variable - so it is vital that calls
  75.  * of the two routines are matched.
  76.  *
  77.  * The effect of this is quite simple; if the nlevels variable
  78.  * is >0, the "do*" routines will not throw away the previous
  79.  * state before making a change; and thus we are able to undo
  80.  * multiple changes.
  81.  *
  82.  * All the do* routines, and start_command(), will throw away
  83.  * the previous saved state if the b_nlevels variable is 0.
  84.  */
  85. bool_t
  86. start_command(window)
  87. Xviwin    *window;
  88. {
  89.     Buffer    *buffer;
  90.  
  91.     buffer = window->w_buffer;
  92.  
  93.     if (buffer->b_nlevels == 0) {
  94.     if (not_editable(buffer)) {
  95.         show_error(window, "Edit not allowed!");
  96.         return(FALSE);
  97.     }
  98.     free_changes(buffer->b_change);
  99.     buffer->b_change = NULL;
  100.     }
  101.  
  102.     buffer->b_nlevels += 1;
  103.  
  104.     total_lines = 0;
  105.  
  106.     save_position(window);
  107.  
  108.     return(TRUE);
  109. }
  110.  
  111. /*
  112.  * Save the cursor position.
  113.  *
  114.  * This is called at the start of each change, so that
  115.  * the cursor will return to the right place after an undo.
  116.  */
  117. static void
  118. save_position(window)
  119. Xviwin    *window;
  120. {
  121.     Buffer    *buffer;
  122.     Change    *change;
  123.  
  124.     buffer = window->w_buffer;
  125.  
  126.     change = challoc();
  127.     if (change == NULL)
  128.     return;
  129.  
  130.     change->c_type = C_POSITION;
  131.     change->c_pline = lineno(buffer, window->w_cursor->p_line);
  132.     change->c_pindex = window->w_cursor->p_index;
  133.  
  134.     change->c_next = buffer->b_change;
  135.     buffer->b_change = change;
  136. }
  137.  
  138. void
  139. end_command(window)
  140. Xviwin    *window;
  141. {
  142.     Buffer    *buffer;
  143.  
  144.     buffer = window->w_buffer;
  145.  
  146.     if (buffer->b_nlevels > 0) {
  147.     buffer->b_nlevels -= 1;
  148.     if (buffer->b_nlevels == 0) {
  149.         report(window);
  150.     }
  151.     } else {
  152.     show_error(window, "Internal error: too many \"end_command\"s");
  153.     }
  154. }
  155.  
  156. /*
  157.  * Replace the given section of the given line with the
  158.  * new (null-terminated) string. nchars may be zero for
  159.  * insertions of text; newstring may point to a 0-length
  160.  * string to delete text. start is the first character
  161.  * of the section which is to be replaced.
  162.  *
  163.  * Note that we don't call strcpy() to copy text here, for
  164.  * two reasons: firstly, we are likely to be copying small
  165.  * numbers of characters and it is therefore faster to do
  166.  * the copying ourselves, and secondly, strcpy() doesn't
  167.  * necessarily work for copying overlapping text towards
  168.  * higher locations in memory.
  169.  */
  170. void
  171. replchars(window, line, start, nchars, newstring)
  172. Xviwin    *window;
  173. Line    *line;
  174. int    start;
  175. int    nchars;
  176. char    *newstring;
  177. {
  178.     register char    *from;        /* where to copy from */
  179.     register char    *to;        /* where to copy to */
  180.     register int    nlen;        /* length of newstring */
  181.     register int    olen;        /* length of old line */
  182.     register int    offset;        /* how much to move text by */
  183.     Buffer        *buffer;
  184.     Change        *change;
  185.  
  186.     buffer = window->w_buffer;
  187.  
  188.     /*
  189.      * If this is a singleton command, make sure we
  190.      * destroy the changes made as part of the last
  191.      * command before we start the new one.
  192.      */
  193.     if (buffer->b_nlevels == 0) {
  194.     if (not_editable(buffer)) {
  195.         show_error(window, "Edit not allowed!");
  196.         return;
  197.     }
  198.     free_changes(buffer->b_change);
  199.     buffer->b_change = NULL;
  200.     save_position(window);
  201.     }
  202.  
  203.     /*
  204.      * First thing we have to do is to obtain a change
  205.      * structure to record the change so we can be sure
  206.      * that we can undo it. If this fails, we must not
  207.      * make any change at all.
  208.      */
  209.     change = challoc();
  210.     if (change == NULL)
  211.     return;
  212.  
  213.     nlen = strlen(newstring);
  214.     olen = strlen(line->l_text + start);
  215.     if (olen < nchars)
  216.     nchars = olen;
  217.     offset = nlen - nchars;
  218.  
  219.     /*
  220.      * Record the opposite change so we can undo it.
  221.      */
  222.     if (nchars == 0) {
  223.     change->c_type = C_DEL_CHAR;
  224.     } else {
  225.     change->c_type = C_CHAR;
  226.     change->c_chars = alloc((unsigned) nchars + 1);
  227.     if (change->c_chars == NULL) {
  228.         chfree(change);
  229.         State = NORMAL;
  230.         return;
  231.     }
  232.     (void) strncpy(change->c_chars, line->l_text + start, nchars);
  233.     change->c_chars[nchars] = '\0';
  234.     }
  235.     change->c_lineno = lineno(buffer, line);
  236.     change->c_index = start;
  237.     change->c_nchars = nlen;
  238.  
  239.     if (offset > 0) {
  240.     register char    *s;
  241.  
  242.     /*
  243.      * Move existing text along by offset to the right.
  244.      * First make some room in the line.
  245.      */
  246.     if (grow_line(line, offset) == FALSE) {
  247.         free(change->c_chars);
  248.         chfree(change);
  249.         State = NORMAL;
  250.         return;
  251.     }
  252.  
  253.     /*
  254.      * Copy characters backwards, i.e. start
  255.      * at the end of the line rather than at
  256.      * the start.
  257.      */
  258.     from = line->l_text + start;
  259.     to = from + offset + olen + 1;
  260.     s = from + olen + 1;
  261.     while (s > from) {
  262.         *--to = *--s;
  263.     }
  264.  
  265.     } else if (offset < 0) {
  266.  
  267.     /*
  268.      * Move existing text along to the left.
  269.      */
  270.     offset = - offset;
  271.     to = line->l_text + start;
  272.     from = to + offset;
  273.  
  274.     /*
  275.      * Do classic K&R strcpy().
  276.      */
  277.     while ((*to++ = *from++) != '\0') {
  278.         ;
  279.     }
  280.     }
  281.  
  282.     /*
  283.      * Finally, copy the new text into position.
  284.      * Note that we are careful not to copy the
  285.      * null terminator.
  286.      */
  287.     from = newstring;
  288.     to = line->l_text + start;
  289.     while (*from != '\0') {
  290.     *to++ = *from++;
  291.     }
  292.  
  293.     buffer->b_flags |= FL_MODIFIED;
  294.  
  295.     window->w_curs_new = TRUE;
  296.     
  297.     /*
  298.      * Push this change onto the LIFO of changes
  299.      * that form the current command.
  300.      */
  301.     change->c_next = buffer->b_change;
  302.     buffer->b_change = change;
  303. }
  304.  
  305. /*
  306.  * Replace the specified set of lines with the replacement set.
  307.  * The number of lines to be replaced may be 0; the replacement
  308.  * list may be a NULL pointer.
  309.  */
  310. void
  311. repllines(window, line, nolines, newlines)
  312. register Xviwin    *window;
  313. Line        *line;
  314. long        nolines;
  315. Line        *newlines;
  316. {
  317.     register Buffer    *buffer;    /* buffer window is mapped onto */
  318.     Line        *firstp;    /* line before first to delete */
  319.     Line        *lastp;        /* line after last to delete */
  320.     Line        *lastline;    /* last line to delete */
  321.     Line        *new_start;    /* start of lines to be inserted */
  322.     Line        *new_end;    /* last line to be inserted */
  323.     long        nnlines;    /* no. new logical lines */
  324.     long        oplines;    /* no. physical lines to be replaced */
  325.     long        nplines;    /* no. new physical lines */
  326.     long        n;        /* lines done so far */
  327.     register Xviwin    *wp;        /* loop variable */
  328.     Change        *change;
  329.  
  330.     buffer = window->w_buffer;
  331.  
  332.     /*
  333.      * If this is a singleton com